asyncapi: '3.0.0'
info:
  title: Forex Connector
  version: '1'
  description: |
    AsyncAPI definition for forex (foreign-exchange) operations. This connector accepts
    an `ExchangeCurrency` command to book a foreign-exchange conversion with an
    external provider (CurrencyCloud), publishes `CurrencyExchanged` on success or
    `CurrencyExchangeFailed` on failure, and continuously publishes `RateUpdated`
    events consumed by downstream services.

    :::warning
    Please note that all the *Commands* and *Events* are wrapped into *Envelope* before being dispatched via Channels.
    :::

    Specific properties that are present in the metadata section of the envelope varied per message.
    The list of required metadata properties is provided in the header's documentation of the corresponding message.

    See this <Link href="/docs/index.html">page</Link> for the more details on the *Envelope* structure and other cross-cutting concerns.

operations:
  # commands
  ExchangeCurrency:
    action: receive
    channel:
      $ref: '#/channels/currencycloud.forex.command.exchange-currency'
    messages:
      - $ref: '#/channels/currencycloud.forex.command.exchange-currency/messages/ExchangeCurrencyCommand'

  # events
  CurrencyExchanged:
    action: send
    channel:
      $ref: '#/channels/forex.event.currency-exchanged'
    messages:
      - $ref: '#/channels/forex.event.currency-exchanged/messages/CurrencyExchangedEvent'

  CurrencyExchangeFailed:
    action: send
    channel:
      $ref: '#/channels/forex.event.currency-exchange-failed'
    messages:
      - $ref: '#/channels/forex.event.currency-exchange-failed/messages/CurrencyExchangeFailedEvent'

  RateUpdated:
    action: send
    channel:
      $ref: '#/channels/forex.event.rate-updated'
    messages:
      - $ref: '#/channels/forex.event.rate-updated/messages/RateUpdatedEvent'

channels:
  currencycloud.forex.command.exchange-currency:
    address: currencycloud.forex.command.exchange-currency
    x-eventcatalog-role: client
    messages:
      ExchangeCurrencyCommand:
        $ref: '#/components/messages/ExchangeCurrencyCommand'

  forex.event.currency-exchanged:
    address: forex.event.currency-exchanged
    x-eventcatalog-role: client
    messages:
      CurrencyExchangedEvent:
        $ref: '#/components/messages/CurrencyExchangedEvent'

  forex.event.currency-exchange-failed:
    address: forex.event.currency-exchange-failed
    x-eventcatalog-role: client
    messages:
      CurrencyExchangeFailedEvent:
        $ref: '#/components/messages/CurrencyExchangeFailedEvent'

  forex.event.rate-updated:
    address: forex.event.rate-updated
    x-eventcatalog-role: client
    messages:
      RateUpdatedEvent:
        $ref: '#/components/messages/RateUpdatedEvent'

components:
  messages:
    # Commands
    ExchangeCurrencyCommand:
      name: ExchangeCurrency
      title: Exchange Currency Command
      summary: Command to book a foreign-exchange conversion
      description: Instructs the connector to book a foreign-exchange conversion between two accounts with an external provider
      x-eventcatalog-message-type: command
      headers:
        $ref: '#/components/schemas/DefaultDSFMetaData'
      payload:
        $ref: '#/components/schemas/ExchangeCurrency'

    # Events
    CurrencyExchangedEvent:
      name: CurrencyExchanged
      title: Currency Exchanged Event
      summary: Event indicating that a foreign-exchange conversion has been booked successfully
      description: Published when a foreign-exchange conversion has been booked successfully by the external provider
      x-eventcatalog-message-type: event
      headers:
        $ref: '#/components/schemas/DefaultDSFMetaData'
      payload:
        $ref: '#/components/schemas/CurrencyExchanged'

    CurrencyExchangeFailedEvent:
      name: CurrencyExchangeFailed
      title: Currency Exchange Failed Event
      summary: Event indicating that a foreign-exchange conversion has failed
      description: Published when a foreign-exchange conversion could not be completed
      x-eventcatalog-message-type: event
      headers:
        $ref: '#/components/schemas/DefaultDSFMetaData'
      payload:
        $ref: '#/components/schemas/CurrencyExchangeFailed'

    RateUpdatedEvent:
      name: RateUpdated
      title: Rate Updated Event
      summary: Event indicating that the FX rate for a specific currency pair has changed
      description: |
        Published when the upstream FX rate for a specific currency pair has moved beyond the configured threshold.
        Both directions of every pair are published as separate events (e.g. EUR→USD and USD→EUR).
        Consumers use the `asOf` field to enforce monotonic ordering when updating their local rate cache.
      x-eventcatalog-message-type: event
      contentType: application/avro
      headers:
        $ref: '#/components/schemas/DefaultDSFMetaData'
      payload:
        $ref: '#/components/schemas/RateUpdated'

  schemas:
    # Base Types
    Message:
      type: object
      description: Base message type that all commands and events inherit from
      required:
        - id
      properties:
        id:
          type: string
          description: Unique identifier for the message, typically a UUID
          default: UUID generated string

    Command:
      description: Base type for all commands in the system
      allOf:
        - $ref: '#/components/schemas/Message'

    Event:
      description: Base type for all events in the system
      allOf:
        - $ref: '#/components/schemas/Message'

    # Headers
    DefaultDSFMetaData:
      type: object
      description: Details to be specified in the {"metadata"} section of the message Envelope
      required:
        - timestamp
        - properties
      properties:
        timestamp:
          type: string
          format: date-time
          description: |
            UTC timestamp when the event was created.
            Uses ISO 8601 format with nanosecond precision.
            Example: 2025-09-03T08:24:54.310639669Z
        properties:
          $ref: '#/components/schemas/DefaultDSFMetaDataProperties'

    DefaultDSFMetaDataProperties:
      type: object
      description: Set of {"properties"} to be specified in the {"metadata"} section of the message Envelope
      required:
        - plmr-correlation-id
      properties:
        plmr-correlation-id:
          type: string
          description: Unique identifier for tracking and correlating events with the originating commands
          default: UUID generated string

    # Shared Value Objects
    Debit:
      type: object
      description: Debit side of a currency exchange (the currency being sold)
      required:
        - currency
      properties:
        currency:
          type: string
          description: Currency being debited, in ISO 4217 format
        accountId:
          type: string
          format: uri
          description: |
            Unique identifier for the debited account. The URI format is used to identify the underlying core
            (i.e. `<core-name>:<identifier>`). Optional when the debit account is not represented in the platform.

    Credit:
      type: object
      description: Credit side of a currency exchange (the currency being bought)
      required:
        - currency
      properties:
        currency:
          type: string
          description: Currency being credited, in ISO 4217 format
        accountId:
          type: string
          format: uri
          description: |
            Unique identifier for the credited account. The URI format is used to identify the underlying core
            (i.e. `<core-name>:<identifier>`). Optional when the credit account is not represented in the platform.

    Debited:
      type: object
      description: Details of the debited side of a completed currency exchange
      required:
        - currency
        - amount
      properties:
        accountId:
          type: string
          format: uri
          description: |
            Unique identifier for the debited account. The URI format is used to identify the underlying core
            (i.e. `<core-name>:<identifier>`).
        currency:
          type: string
          description: Currency of the debited amount, in ISO 4217 format
        amount:
          type: number
          description: Amount debited from the source account

    Credited:
      type: object
      description: Details of the credited side of a completed currency exchange
      required:
        - currency
        - amount
      properties:
        accountId:
          type: string
          format: uri
          description: |
            Unique identifier for the credited account. The URI format is used to identify the underlying core
            (i.e. `<core-name>:<identifier>`).
        currency:
          type: string
          description: Currency of the credited amount, in ISO 4217 format
        amount:
          type: number
          description: Amount credited to the destination account

    FixedAmountSide:
      type: string
      description: |
        Indicates which side of the exchange is fixed. When `DEBIT`, the debit amount is fixed and the credit
        amount is derived from the rate. When `CREDIT`, the credit amount is fixed and the debit amount is
        derived from the rate.
      enum:
        - DEBIT
        - CREDIT

    Failure:
      type: object
      description: Details about the failure
      required:
        - code
        - reason
      properties:
        code:
          type: string
          description: The error code identifying the type of failure
          enum:
            - "urn:plmr:forex:connector:EXCHANGE_CURRENCY:CURRENCY_EXCHANGE_FAILED"
        reason:
          type: string
          description: Human-readable description of the failure

    # Command Payloads
    ExchangeCurrency:
      allOf:
        - $ref: '#/components/schemas/Command'
        - type: object
          description: Command to book a foreign-exchange conversion between two accounts
          required:
            - debit
            - credit
            - fixedAmountSide
            - amount
          properties:
            debit:
              $ref: '#/components/schemas/Debit'
              description: Debit side of the exchange (currency being sold)
            credit:
              $ref: '#/components/schemas/Credit'
              description: Credit side of the exchange (currency being bought)
            fixedAmountSide:
              $ref: '#/components/schemas/FixedAmountSide'
              description: Which side of the exchange is the fixed amount
            amount:
              type: number
              description: The fixed amount, expressed in the currency of the side identified by `fixedAmountSide`

    # Event Payloads
    CurrencyExchanged:
      allOf:
        - $ref: '#/components/schemas/Event'
        - type: object
          description: Event published when a foreign-exchange conversion has been booked successfully
          required:
            - conversionId
            - debited
            - credited
            - fixedAmountSide
            - rate
          properties:
            conversionId:
              type: string
              description: Identifier assigned by the external provider to the completed conversion
            debited:
              $ref: '#/components/schemas/Debited'
              description: Details of the debited side of the conversion
            credited:
              $ref: '#/components/schemas/Credited'
              description: Details of the credited side of the conversion
            fixedAmountSide:
              $ref: '#/components/schemas/FixedAmountSide'
              description: Which side of the exchange was the fixed amount
            rate:
              type: number
              description: Exchange rate applied to the conversion

    CurrencyExchangeFailed:
      allOf:
        - $ref: '#/components/schemas/Event'
        - type: object
          description: Event published when a foreign-exchange conversion could not be completed
          required:
            - failure
          properties:
            debit:
              $ref: '#/components/schemas/Debit'
              description: Debit side of the attempted exchange, if known
            credit:
              $ref: '#/components/schemas/Credit'
              description: Credit side of the attempted exchange, if known
            fixedAmountSide:
              $ref: '#/components/schemas/FixedAmountSide'
              description: Which side of the exchange was the fixed amount, if known
            amount:
              type: number
              description: The fixed amount from the original command, if known
            failure:
              $ref: '#/components/schemas/Failure'
              description: Details about the failure

    # Avro Event Payloads
    RateUpdated:
      schemaFormat: 'application/vnd.apache.avro;version=1.9.0'
      schema:
        $ref: './RateUpdated.avsc'